/***************************************************************************
 创建者: 华磊
 开始时间: 2019.3.9
 copyright: (C) 华友高科
 修改说明: (每次有修改就添加一条，带有 修改人，修改时间，修改描述)
 example (1) hualei 2017.5.8 类的具体实现代码编写

 ***************************************************************************/
#include "modbusmaster.h"
#include <QModbusTcpClient>
#include <QModbusRtuSerialMaster>
#include <qthread.h>
#include <QUrl>



ModbusMaster::ModbusMaster(ModbusConnection typeIn, QString portNameIn, SerialSettings serialSettingIn
                           , QString ip_port_in)
{

    type=typeIn;
    portName=portNameIn;
    serialSetting=serialSettingIn;
    ipAndPort=ip_port_in;
    modbusDevice=NULL;
    initialModbusMaster();
    readResult=READ_WRITE_RESULT_STATE_IDLE;
    writeResult=READ_WRITE_RESULT_STATE_IDLE;

}


ModbusMaster::~ModbusMaster()
{
    if (modbusDevice)
        modbusDevice->disconnectDevice();
    delete modbusDevice;
}


int ModbusMaster::initialModbusMaster( )
{
    if (modbusDevice) {
        modbusDevice->disconnectDevice();
        delete modbusDevice;
        modbusDevice = nullptr;
    }

    if (type == Serial) {
        modbusDevice = new QModbusRtuSerialMaster(this);
    } else if (type == Tcp) {
        modbusDevice = new QModbusTcpClient(this);

    }

    connect(modbusDevice, &QModbusClient::errorOccurred,this,&ModbusMaster::onErrorOccured);

    if (!modbusDevice) {
//        ui->connectButton->setDisabled(true);
//        if (type == Serial)
//            statusBar()->showMessage(tr("Could not create Modbus master."), 5000);
//        else
//            statusBar()->showMessage(tr("Could not create Modbus client."), 5000);
    } else {
        connect(modbusDevice, &QModbusClient::stateChanged,
                this, &ModbusMaster::onStateChanged);
    }
    return 1;
}

int ModbusMaster::connectPort()
{
    if (!modbusDevice)
        return -1;

    if (modbusDevice->state() != QModbusDevice::ConnectedState) {
        if (type == Serial) {
            modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,
                portName);
            modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,
                serialSetting.parity);
            modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,
                serialSetting.baud);
            modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,
                serialSetting.dataBits);
            modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,
                serialSetting.stopBits);
        } else {
            const QUrl url = QUrl::fromUserInput(ipAndPort);
            modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, url.port());
            modbusDevice->setConnectionParameter(QModbusDevice::NetworkAddressParameter, url.host());
            qDebug()<<"url.port()"<<url.port()<<" url.host()"<<url.host();
        }
        modbusDevice->setTimeout(serialSetting.responseTime);
        modbusDevice->setNumberOfRetries(serialSetting.numberOfRetries);
        if (!modbusDevice->connectDevice())
        {
            return -1;
        }
        else
        {
            readResult=READ_WRITE_RESULT_STATE_IDLE;
            writeResult=READ_WRITE_RESULT_STATE_IDLE;
            qDebug()<<"ModbusMaster::connectPort连接成功"<<modbusDevice->state();
            return 1;
        }
    } else {
        return -1;
    }
    return -1;
}

int ModbusMaster::disConnectPort()
{
    if (!modbusDevice)
        return -1;

    if (modbusDevice->state() != QModbusDevice::ConnectedState)
    {
        return -1;
    }
    else {
                modbusDevice->disconnectDevice();
                return 1;
            }

    return -1;
}



int ModbusMaster::getModbusError()
{
    return modbusDevice->error();
}

int ModbusMaster::getConnectState()
{
//    qDebug()<<modbusDevice->state();
    return modbusDevice->state();
}

READ_WRITE_RESULT_STATE ModbusMaster::getWriteModbusResult()
{
    return writeResult;
}

READ_WRITE_RESULT_STATE ModbusMaster::getReadModbusDataUnit(QModbusDataUnit &modbusDateUnit)
{
    modbusDateUnit=readDataUnit;
    if(READ_WRITE_RESULT_STATE_SUCCESS==readResult)
    {

        return READ_WRITE_RESULT_STATE_SUCCESS;
    }
    else if(READ_WRITE_RESULT_STATE_ERROR==readResult)
    {
        return READ_WRITE_RESULT_STATE_ERROR;
    }
    else {
        return readResult;
    }
    return readResult;
}

int ModbusMaster::readModbusDataUnit_block( QModbusDataUnit &modbusDateUnit, int serverId)
{
    if(-1==readModbusDataUnit(modbusDateUnit,serverId) )
    {
        return -1;
    }
    else
    {
        while(READ_WRITE_RESULT_STATE_WAITING==readResult)
        {
            QCoreApplication::processEvents();
            QThread::msleep(50);
            if(READ_WRITE_RESULT_STATE_SUCCESS==readResult)
            {
                modbusDateUnit=readDataUnit;
                return 1;
            }
            else if(READ_WRITE_RESULT_STATE_ERROR==readResult)
            {
                return -2;
            }
         }

    }
    return -1;
}

int ModbusMaster::writeModbusDataUnit_block(const QModbusDataUnit &modbusDateUnit, int serverId)
{
    if(-1==writeModbusDataUnit(modbusDateUnit,serverId) )
    {
        return -1;
    }
    else
    {
        while(READ_WRITE_RESULT_STATE_WAITING==writeResult)
        {
            QCoreApplication::processEvents();
            QThread::msleep(50);
            if(READ_WRITE_RESULT_STATE_SUCCESS==writeResult)
            {
                return 1;
            }
            else if(READ_WRITE_RESULT_STATE_ERROR==writeResult)
            {
                return -2;
            }
         }

    }
    return -1;
}

void ModbusMaster::onStateChanged(int state)
{
    bool connected = (state != QModbusDevice::UnconnectedState);
//    ui->actionConnect->setEnabled(!connected);
//    ui->actionDisconnect->setEnabled(connected);

    if (state == QModbusDevice::UnconnectedState)
    {

    }
//        ui->connectButton->setText(tr("Connect"));
    else if (state == QModbusDevice::ConnectedState)
    {

    }
//        ui->connectButton->setText(tr("Disconnect"));
}

void ModbusMaster::onErrorOccured(QModbusDevice::Error error)
{

}

int ModbusMaster::readModbusDataUnit(const QModbusDataUnit &modbusDateUnit, int serverId)
{
    if (!modbusDevice)
    {
        readResult=READ_WRITE_RESULT_STATE_ERROR;
        return -1;
    }

    if (QModbusDevice::ConnectedState!=modbusDevice->state())
    {
        readResult=READ_WRITE_RESULT_STATE_ERROR;
        return -1;
    }

    if (READ_WRITE_RESULT_STATE_WAITING==readResult)
    {
//        qDebug()<<"warn:readModbusDataUnit failed,上次读取还没有完成";
        return -1;
    }
    if (READ_WRITE_RESULT_STATE_WAITING==writeResult)
    {
//        qDebug()<<"warn:readModbusDataUnit failed,上次写还没有完成";
        return -2;
    }
    readResult=READ_WRITE_RESULT_STATE_WAITING;
    if (QModbusReply *reply = modbusDevice->sendReadRequest(modbusDateUnit,serverId) )
    {
        if (!reply->isFinished())
        {
//            qDebug()<<"modbusDevice->sendReadRequest";

            connect(reply, &QModbusReply::finished, this, &ModbusMaster::readReady);
            return 1;
        }
        else
            delete reply; // broadcast replies return immediately
    }
    else
    {
        readResult=READ_WRITE_RESULT_STATE_ERROR;
        return -1;
    }

    return -1;
}

void ModbusMaster::readReady()
{
//    qDebug()<<"readReady()";
    QModbusReply* reply = qobject_cast<QModbusReply *>(sender());
    if (!reply)
        return;

    if (reply->error() == QModbusDevice::NoError) {
        readDataUnit=reply->result();
        readResult=READ_WRITE_RESULT_STATE_SUCCESS;
        emit readSucess_signal();

    } else if (reply->error() == QModbusDevice::ProtocolError) {
       readResult=READ_WRITE_RESULT_STATE_ERROR;
    } else {
        readResult=READ_WRITE_RESULT_STATE_ERROR;
    }

    reply->deleteLater();
}

int ModbusMaster::writeModbusDataUnit(const QModbusDataUnit &modbusDateUnit, int serverId)
{
    if (!modbusDevice)
    {
        writeResult=READ_WRITE_RESULT_STATE_ERROR;
        return -1;
    }

    if (QModbusDevice::ConnectedState!=modbusDevice->state())
    {
        readResult=READ_WRITE_RESULT_STATE_ERROR;
        return -2;
    }

    if (READ_WRITE_RESULT_STATE_WAITING==readResult)
    {
//        qDebug()<<"warn:writeModbusDataUnit failed,上次读取还没有完成";
        return -3;
    }
    if (READ_WRITE_RESULT_STATE_WAITING==writeResult)
    {
//        qDebug()<<"warn:writeModbusDataUnit failed,上次写还没有完成";
        return -4;
    }
    writeResult=READ_WRITE_RESULT_STATE_WAITING;
    if (auto *reply = modbusDevice->sendWriteRequest(modbusDateUnit, serverId))
    {
        if (!reply->isFinished())
        {

            connect(reply, &QModbusReply::finished, this, [this, reply]()
            {
                if (reply->error() == QModbusDevice::ProtocolError)
                {
                   writeResult=READ_WRITE_RESULT_STATE_ERROR;
                } else if (reply->error() != QModbusDevice::NoError)
                {
                     writeResult=READ_WRITE_RESULT_STATE_ERROR;
                }
                else if (reply->error() == QModbusDevice::NoError)
                {
                     writeResult=READ_WRITE_RESULT_STATE_SUCCESS;
                }
                reply->deleteLater();
            });
            return 1;
        }
        else
        {
            // broadcast replies return immediately
            reply->deleteLater();
            writeResult=READ_WRITE_RESULT_STATE_SUCCESS;
            return 1;
        }
    }
    else
    {
        writeResult=READ_WRITE_RESULT_STATE_ERROR;
        return -5;
    }
    return -6;
}


